const { Op, Transaction } = require('sequelize')
const Mysequelize = require('@/models')
const R = require('@/models/Response')
const { User, UserActiveExam } = require('@/models/model')
const client = require('@/framework/redis/index')
const { wpHset, wpSet, wpDel, wpGet } = require('@/framework/redis/redisFn')
const { USER_TOKEN, USER_SETTINGS } = require('@/constants/user')
const { ERROR_USER_CODE } = require('@/constants/errorCode')
const {
  SEPARATE_CHAR,
  FIVE_MINUTES,
  businessTypes
} = require('@/constants/index')
const { userLoginToken } = require('@/config/config.default')
const { createToken } = require('@/utils/jwt')
const { generatId } = require('@/utils/idUtil')
const { delModel } = require('@/utils/controllerCommon')
const { sendMail } = require('@/utils/sendMailCode')
const { genSalt, MD5 } = require('@/utils/md5')
const { getNewPwd } = require('@/utils/common')
const { getSlide, getMathValidCode } = require('@/utils/slideCode')

/**
 * 用户注册
 * @param {*} ctx
 */
module.exports.register = async (ctx) => {
  const t = await Mysequelize.transaction()
  const { username, password, phone, email } = ctx.request.body
  let nextId = generatId()
  const salt = genSalt()
  const newPwd = await MD5(password, salt)
  const createObj = {
    salt,
    username,
    password: newPwd,
    avator: '',
    nickname: username,
    createUser: nextId,
    updateUser: nextId,
    id: nextId,
    roleId: 0,
    sex: 0,
    isActive: 1
  }
  phone && (createObj.phone = phone)
  email && (createObj.email = email)
  try {
    const user = await User.create(createObj, { transaction: t })
    await t.commit()
    ctx.body = R.success()
  } catch (err) {
    console.log(err)

    await t.rollback()
    ctx.body = R.error(ERROR_USER_CODE.CREATE_USER_ERROR)
    return
  }
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 生成token
 * @param {*} ctx
 */
const genToken = async (ctx) => {
  console.log(ctx.user, 'user')

  const token = await createToken(
    ctx.user,
    userLoginToken.secret,
    userLoginToken.tokenExpired
  )
  const reflesh_token = await createToken(
    ctx.user,
    userLoginToken.refleshSecret,
    userLoginToken.refleshTokenExpired
  )

  await wpHset(USER_TOKEN, ctx.user.id, { token, reflesh_token })
  ctx.body = R.success({
    token,
    reflesh_token
  })
}

/**
 * 用户登录
 * @param {*} ctx
 */
module.exports.login = async (ctx) => {
  // 返回token和刷新token
  // 返回当前用户的系统角色--->对应的功能开关
  await genToken(ctx)
}

/**
 * 用户刷新token
 * @param {*} ctx
 */
module.exports.reflesh = async (ctx) => {
  // 到达这里，说明刷新token还未过期
  // 生成新的token和refleshtoken
  await genToken(ctx)
}

/**
 * 用户退出登录的接口
 * @param {} ctx
 */
module.exports.loginOut = async (ctx) => {
  // 到达这里说明token是正确的，我们需要清除调token（redis）
  await client.HDEL(USER_TOKEN, ctx.user.id)
  ctx.body = R.success()
}

/**
 * 检查用户信息是否重复接口
 * @param {*} ctx
 */
module.exports.checkUserInfo = async (ctx) => {
  // 到达这里，说明验证通过了
  ctx.body = R.success()
}

/**
 * 查询用户个人信息的接口
 * @param {*} ctx
 */
module.exports.getUserInfo = async (ctx) => {
  const user = await User.findByPk(BigInt(ctx.user.id), {
    attributes: ['id', 'username', 'nickname', 'avator', 'email', 'roleId']
  })

  const finaUser = user.toJSON()

  if (+user.roleId === 1) {
    finaUser.role = {
      name: '管理员',
      Permissions: [
        {
          id: 1,
          name: '管理员',
          ename: 'admin'
        }
      ]
    }
  } else {
    finaUser.role = {
      name: '用户',
      Permissions: []
    }
  }

  ctx.body = R.success(finaUser)
}

/**
 * 更新用户信息接口
 * @param {*} ctx
 */
module.exports.updateUserInfo = async (ctx) => {
  // 走到这里说明信息没有重复，可以进行修改
  const t = await Mysequelize.transaction()
  const { nickname, avator, phone, email, password } = ctx.request.body
  const updateObj = {}
  nickname && (updateObj.nickname = nickname)
  avator && (updateObj.avator = avator)
  phone && (updateObj.phone = phone)
  email && (updateObj.email = email)
  if (password) {
    const salt = genSalt()
    const newPwd = await MD5(password, salt)
    updateObj.password = newPwd
    updateObj.salt = salt
  }

  try {
    await User.update(updateObj, {
      where: {
        id: BigInt(ctx.user.id)
      },
      transaction: t
    })
    await t.commit()
    // 修改邮箱时需要验证码，成功后把验证码清除
    if (email) {
      await wpDel('bindEmail-code-' + email)
    }
    ctx.body = R.success()
  } catch {
    await t.rollback()
    ctx.body = R.error(ERROR_USER_CODE.UPDATE_USERINFO_ERROR)
  }
}

/**
 * 用户密码找回接口
 * @param {*} ctx
 */
module.exports.userFindPwd = async (ctx) => {
  // 到达这里，说明邮箱，验证码，密码没有问题，可以进行更新了
  const { email, password } = ctx.request.body
  const { newPwd, salt } = await getNewPwd(password)
  const t = await Mysequelize.transaction()
  const user = await User.findOne({
    where: {
      email
    }
  })
  try {
    await User.update(
      {
        password: newPwd,
        salt
      },
      {
        where: {
          email
        },
        transaction: t
      }
    )

    await t.commit()
    ctx.logs = {
      userId: user.id,
      username: user.username
    }
    ctx.body = R.success()
  } catch {
    await t.rollback()
    ctx.body = R.error(ERROR_USER_CODE.UPDATE_PWD_ERROR)
  }
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 批量启用/禁用用户接口
 * @param {*} ctx
 */
module.exports.setUserStatus = async (ctx) => {
  let { userIds, status } = ctx.request.body
  const t = await Mysequelize.transaction()
  userIds = userIds.map((v) => BigInt(v))
  try {
    await User.update(
      {
        isActive: +status
      },
      {
        where: {
          id: {
            [Op.in]: userIds
          }
        },
        transaction: t
      }
    )
    await t.commit()
    ctx.body = R.success()
  } catch (error) {
    await t.rollback()
    ctx.body = R.error(ERROR_USER_CODE.USER_STATUS_ERROR)
  }
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 删除用户接口
 * @param {*} ctx
 */
module.exports.delUsers = async (ctx) => {
  await delModel(
    User,
    ERROR_USER_CODE.USER_DEL_ERROR,
    'userIds'
  )(ctx, [{ model: UserActiveExam, idName: 'userId' }])
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description:查询用户接口
 * @param {*} ctx
 */
module.exports.listUsers = async (ctx) => {
  const { page, pageSize, username } = ctx.query
  const whereCondition = {}
  username &&
    (whereCondition.username = {
      [Op.like]: `%${username}%`
    })

  const start = (page - 1) * pageSize
  const { rows, count } = await User.findAndCountAll({
    where: whereCondition,
    // raw: true,
    offset: +start,
    limit: +pageSize,
    distinct: true,
    order: [['createTime', 'DESC']]
  })

  ctx.body = R.success({
    page,
    pageSize,
    total: count,
    pages: Math.ceil(count / pageSize),
    items: rows
  })
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 给用户发送邮箱验证码的内部方法
 * @param {*} ctx
 */

module.exports.sendEmailCodeUtil = async (
  mail,
  methodType,
  businessType,
  field
) => {
  // 根据类型的不同，进行手机号或者邮箱发送

  // code码在redis的格式，businessType-code={
  // userId----code  expired
  // }
  const code = Math.random().toString().slice(2, 6)

  // 汇总一下可能的业务类型
  if (methodType === 'email') {
    // 邮箱发送
    const res = await new Promise((resolve) => {
      sendMail(
        mail,
        businessTypes.find((item) => item.type === businessType).subject,
        code,
        (status) => resolve(status)
      )
    })

    if (res) {
      await wpSet(businessType + '-code-' + field, code, FIVE_MINUTES)
    }

    return res
  } else if (methodType === 'phone') {
    //
    return false
  }
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 给用户发送邮箱验证码的接口
 * @param {*} ctx
 */

module.exports.sendEmailCode = async (ctx) => {
  const { email, businessType, methodType } = ctx.request.body

  // 将emial或者phone作为key，businessType-code-[email|phone]
  const res = await this.sendEmailCodeUtil(
    email,
    methodType,
    businessType,
    email
  )

  if (!res) {
    ctx.body = R.error(ERROR_USER_CODE.CODE_SEND_ERROR)
    return
  }

  ctx.body = R.success()
}

/**
 * @author: Hejh(3010733382@qq.com)
 * @description: 给用户发送滑块验证码的接口
 * @param {*} ctx
 */
module.exports.sendSlideCode = async (ctx) => {
  const { ip, ua } = ctx.mySession

  // 将ip+ua作为key，slide-code-[ip+ua]
  const res = await getMathValidCode()

  const { text, img } = res

  // redis存储验证码
  await wpSet('slide-code-' + ip + ua, text, FIVE_MINUTES)
  ctx.body = R.success({
    img
  })
}

/**
 * 查询用户的权限列表
 */
module.exports.listUserPermissions = async (ctx) => {
  const user = await User.findByPk(BigInt(ctx.user.id))
  let permission = ['user']
  if (+user.roleId === 1) {
    permission = ['admin']
  }
  ctx.body = R.success(permission)
}
